
package dtu.app.activity;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.YuvImage;
import android.hardware.Camera.Size;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.iqengines.demo.R;
import com.iqengines.sdk.IQE;
import com.iqengines.sdk.IQE.OnResultCallback;

import dtu.app.db.ProductDatabaseHelper;
import dtu.app.model.Product;

public class ScanActivity extends Activity {

	static final String KEY = "f266576ae21248229dd7197ca3b25787";
	static final String SECRET = "87e3c60615784cf0bd2ce47d9ad6dbbf";

	// Activates the local search.
	static final boolean SEARCH_OBJECT_LOCAL = true;

	// Activates the barcode scanning
	static boolean SEARCH_OBJECT_BARCODE = true;

	// Activates the scan search.
	static boolean SEARCH_OBJECT_SCAN = true;

	// Activates the snap search
	static boolean SEARCH_OBJECT_SNAP = true;

	// Activates the remote search.
	static final boolean SEARCH_OBJECT_REMOTE = true;

	// Maximum duration of a remote search.
	static final long REMOTE_MATCH_MAX_DURATION = 10000;

	//	static final int MAX_ITEM_HISTORY = 20;
	static final boolean PROCESS_ASYNC = true;
	static final boolean DEBUG = true;
	static IQE iqe;

	private static final String TAG = ScanActivity.class.getSimpleName();
	private Preview preview;
	private AtomicBoolean activityRunning = new AtomicBoolean(false);
	private Preview.FrameReceiver mreceiver;
	
	private JSONParser jsonParser;
	private ProductDatabaseHelper database;
	private LayoutInflater inflater;
	private Button scanButton1x;
	private Button scanButton3x;
	private Button scanButton5x;
	private MediaPlayer mp;
	private boolean scanButtonPressed = false;
	private LastButton lastButtonPressed;
	
	enum LastButton {
		button1x,
		button3x,
		button5x
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (DEBUG) Log.d(TAG, "onCreate");
		ActionBar actionBar = getActionBar();
		actionBar.setDisplayHomeAsUpEnabled(true);
		actionBar.setHomeButtonEnabled(true);
		setContentView(R.layout.main);
		setTitle("Scan Groceries");
		
		initUI();
		initIqSdk();
		mreceiver = new CameraFrameReceiver();
		preview.mPreviewThread = preview.new PreviewThread("Preview Thread");
		preview.mPreviewThread.start();
		
		
		scanButton1x = (Button)findViewById(R.id.btnScan1x);
		scanButton1x.setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View arg0, MotionEvent arg1) {
				switch(arg1.getAction())
				{
					case MotionEvent.ACTION_DOWN:
						scanButtonPressed = true;
						lastButtonPressed = LastButton.button1x;
						startScanning();
						((Button)arg0).setPressed(true);
						return true;
					case MotionEvent.ACTION_UP:
					case MotionEvent.ACTION_CANCEL:
						scanButtonPressed = false;
						lastButtonPressed = LastButton.button1x;
						stopScanning();
						((Button)arg0).setPressed(false);
						return true;
				}
				return false;
			}
		});
		
		scanButton3x = (Button)findViewById(R.id.btnScan3x);
		scanButton3x.setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View arg0, MotionEvent arg1) {
				switch(arg1.getAction())
				{
					case MotionEvent.ACTION_DOWN:
						scanButtonPressed = true;
						lastButtonPressed = LastButton.button3x;
						startScanning();
						((Button)arg0).setPressed(true);
						return true;
					case MotionEvent.ACTION_UP:
					case MotionEvent.ACTION_CANCEL:
						scanButtonPressed = false;
						lastButtonPressed = LastButton.button3x;
						stopScanning();
						((Button)arg0).setPressed(false);
						return true;
				}
				return false;
			}
		});
		
		scanButton5x = (Button)findViewById(R.id.btnScan5x);
		scanButton5x.setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View arg0, MotionEvent arg1) {
				switch(arg1.getAction())
				{
					case MotionEvent.ACTION_DOWN:
						scanButtonPressed = true;
						lastButtonPressed = LastButton.button5x;
						startScanning();
						((Button)arg0).setPressed(true);
						return true;
					case MotionEvent.ACTION_UP:
					case MotionEvent.ACTION_CANCEL:
						scanButtonPressed = false;
						lastButtonPressed = LastButton.button5x;
						stopScanning();
						((Button)arg0).setPressed(false);
						return true;
				}
				return false;
			}
		});
	}

	private void initIqSdk() {
		iqe = new IQE(this, SEARCH_OBJECT_REMOTE,SEARCH_OBJECT_LOCAL, SEARCH_OBJECT_BARCODE, onResultCallback, KEY, SECRET);
	}

	@Override
	public void onDestroy() {
		iqe.destroy();
		super.onDestroy();
	}

	private void initUI() {
		preview = (Preview) findViewById(R.id.preview);		
	}

	@Override
	public void onResume() {
		super.onResume();
		activityRunning.set(true);
		iqe.resume();
		preview.setFrameReceiver(mreceiver);
		if(preview.mCamera!=null){
			unfreezePreview();
			
			if(!scanButtonPressed)
				stopScanning();
			else
				startScanning();
		}
	}

	@Override
	public void onPause() {
		if (DEBUG) Log.d(TAG, "onPause");
		stopScanning();
		iqe.pause();
		preview.stopPreview();
		activityRunning.set(false);
		super.onPause();
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.scan_menu, menu);
		return super.onCreateOptionsMenu(menu);
	}
	
	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		switch (item.getItemId()) {
	        case android.R.id.home:
	            finish();
	            return true;
	        case R.id.menu_temp_products:
	    		Intent intent = new Intent(this, TempProductActivity.class);
	            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	            startActivity(intent);
	        	return true;
	        default:
	            return super.onOptionsItemSelected(item);
		}
	}

	private void startScanning() {
		if(SEARCH_OBJECT_SCAN){
			if(DEBUG) Log.d(TAG,"start scanning");
			preview.mPreviewThreadRun.set(true);
			iqe.goScan();
			preview.scan();
		}
	}

	private void stopScanning(){
		if(DEBUG) Log.d(TAG,"stop scanning");
		preview.mPreviewThreadRun.set(false);
	}

	private void unfreezePreview() {
		preview.startPreview();
		if(SEARCH_OBJECT_SCAN){
			startScanning();
		}
	}

	// Method that starts a local research on the phone. onResultCallback is called when result is ready.
	private void processImageScan(final YuvImage yuv) {
		if (DEBUG) Log.d(TAG,"scan decode message");
		iqe.goScan();
		Message.obtain(iqe, IQE.CMD_DECODE, IQE.scan, 0, yuv).sendToTarget();
	}
	
	// Interface used to communicate with the IQE class
	private OnResultCallback onResultCallback = new OnResultCallback() {

		@Override
		public void onQueryIdAssigned(String queryId, String pathName, int callType) {
			// TODO Auto-generated method stub
			// Not necessary to do anything /Jeppe
		}

		@Override
		public void onResult(String queryId, String objId, String objName, String objMeta, int engine, final int callType) {
			
			String json = objMeta.substring(objMeta.indexOf("\"")+1, objMeta.lastIndexOf("\""));
			
			if (null == inflater) inflater = getLayoutInflater();
			
			InputStream is = null;
			try {
				is = getApplicationContext().getAssets().open("iqedata/" + objId + "/" + objId + ".jpg");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
        	Product product = new Product();
			try {
				jsonParser = new JSONParser();
				Object obj = jsonParser.parse(json);
				JSONObject jsonObject = (JSONObject) obj;
				
				product.setName(objName);
				product.setCategory((String) jsonObject.get("category"));
				product.setUnit((String) jsonObject.get("unit"));
				product.setPieces((Long) jsonObject.get("pieces"));
				product.setTotalPieces((Long) jsonObject.get("pieces"));
				product.setPiecesAmount((Double) jsonObject.get("pieces_amount"));
				product.setTotalAmount((Double) jsonObject.get("total_amount"));
				product.setImagePath("iqedata/" + objId + "/" + objId + ".jpg");
				database = new ProductDatabaseHelper(getBaseContext());
				Product existing = database.getProductByName(objName, true);
				
				long totalPieces = product.getPieces();
				double totalAmount = product.getTotalAmount();
				switch(lastButtonPressed)
				{
					case button3x:
						totalPieces *= 3;
						totalAmount *= 3.0;
						break;
					case button5x:
						totalPieces *= 5;
						totalAmount *= 5.0;
						break;
					case button1x:
					default:
						break;
				}
				
				if (existing != null)
				{					
					existing.setTotalPieces(existing.getTotalPieces() + totalPieces);
					existing.setTotalAmount(existing.getTotalAmount() + totalAmount);
					database.updateProduct(existing, true);
				}else {
					product.setTotalAmount(totalAmount);
					product.setTotalPieces(totalPieces);
					database.insertProduct(product, true); // true for insert in tempList
				}

			} catch (ParseException e) {
				Log.e("ScanActivity", "JSON parse error: " + e.getMessage());
			}
			
			mp = MediaPlayer.create(ScanActivity.this, R.raw.beep);
            mp.setOnCompletionListener(new OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    mp.release();
                }
            });   
            mp.start();
			
			View scannedView = inflater.inflate(R.layout.scanned_dialog, null);
			((ImageView)scannedView.findViewById(R.id.scanned_item_image)).setImageBitmap(decodeSampledBitmapFromStream(is, dpToPx(150), dpToPx(150)));
			switch(lastButtonPressed)
			{
				case button3x:
					((TextView)scannedView.findViewById(R.id.scanned_item_description1)).setText("Added 3x of");
					break;
				case button5x:
					((TextView)scannedView.findViewById(R.id.scanned_item_description1)).setText("Added 5x of");
					break;
				case button1x:
				default:
					((TextView)scannedView.findViewById(R.id.scanned_item_description1)).setText("Added one of");
					break;
			}
			((TextView)scannedView.findViewById(R.id.scanned_item_description2)).setText(objName);
			
			Toast toast = new Toast(ScanActivity.this);
			toast.setView(scannedView);
			toast.setGravity(Gravity.BOTTOM, 0, 150);
			toast.setDuration(Toast.LENGTH_SHORT);
			toast.show();
			
			new Thread(new Runnable() {
			    public void run() {
			    	try {
						Thread.sleep(500); //Tweak this for delay between scans
						if (scanButtonPressed)
							startScanning();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			    }
			  }).start();
		}

		@Override
		public void onNoResult(int callType, Exception e, File imgFile) {
			if (scanButtonPressed)
				startScanning();
		}
	};
	
	public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
	    // Raw height and width of image
	    final int height = options.outHeight;
	    final int width = options.outWidth;
	    int inSampleSize = 1;
	
	    if (height > reqHeight || width > reqWidth) {
	
	        // Calculate ratios of height and width to requested height and width
	        final int heightRatio = Math.round((float) height / (float) reqHeight);
	        final int widthRatio = Math.round((float) width / (float) reqWidth);
	
	        // Choose the smallest ratio as inSampleSize value, this will guarantee
	        // a final image with both dimensions larger than or equal to the
	        // requested height and width.
	        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
	    }
	
	    return inSampleSize;
	}
	
	public static Bitmap decodeSampledBitmapFromStream(InputStream is,
	        int reqWidth, int reqHeight) {

	    // First decode with inJustDecodeBounds=true to check dimensions
	    final BitmapFactory.Options options = new BitmapFactory.Options();
	    options.inJustDecodeBounds = true;
	    BitmapFactory.decodeStream(is, null, options);

	    // Calculate inSampleSize
	    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

	    // Decode bitmap with inSampleSize set
	    options.inJustDecodeBounds = false;
	    return BitmapFactory.decodeStream(is, null, options);
	}
	
	public int dpToPx(int dp)
	{
		DisplayMetrics displayMetrics = this.getApplicationContext().getResources().getDisplayMetrics();
		return (int)((dp * displayMetrics.density) + 0.5);
	}

	class CameraFrameReceiver implements Preview.FrameReceiver {

		@Override
		public void onFrameReceived(byte[] frameBuffer, Size framePreviewSize) {

			if (!preview.mPreviewThreadRun.get()) {
				return;
			}

			if(frameBuffer == null){
				if (DEBUG) Log.d(TAG,"no picture");
				return;
			}

			YuvImage yuvImage = new YuvImage(frameBuffer, 17, framePreviewSize.width, framePreviewSize.height, null);
			// analyze the picture.
			processImageScan(yuvImage);
		}
	}

}
